Skip to content

dgb(phase-b): lift PPLNS weight walk into shared SSOT (step 1) - #333

Merged
frstrtr merged 5 commits into
masterfrom
dgb/pplns-weight-walk-ssot
Jun 22, 2026
Merged

dgb(phase-b): lift PPLNS weight walk into shared SSOT (step 1)#333
frstrtr merged 5 commits into
masterfrom
dgb/pplns-weight-walk-ssot

Conversation

@frstrtr

@frstrtr frstrtr commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Stacked on #331. Phase B.

What

Extract generate_share_transaction() step-1 PPLNS tracker walk into the shared SSOT dgb::coin::compute_pplns_weight_walk() — the producer-side counterpart of the steps 2-3 lift (compute_pplns_payout_split, #328). The share-VERIFICATION path and the per-connection Stratum coinbase EMISSION path now walk the tracker through one implementation, so the weight map a miner hashes cannot drift from the one the verifier enforces.

Why

This is the prerequisite for the upcoming main_dgb seam-bind (binding set_pplns_inputs_fn to the live tracker lambda). Collapsing the walk into an SSOT before the bind removes the divergence risk from the bind itself — the bind becomes a thin call into already-verified code.

Faithfulness

  • Verbatim lift: exact V36 depth-decay vs pre-V36 grandparent-start branch, the data.py insufficient-depth guard, and the block-target/spread max_weight cap are byte-unchanged.
  • Result type is deduced from the tracker (decltype) to avoid the share_tracker.hppshare_check.hpp include cycle.
  • New WeightWalkSSOTContract KAT locks the helper contract (verbatim V36 forward + safe-empty on null/absent parent). The unchanged generate_share_transaction fixtures prove value-level byte-identity.

Tests

dgb_share_test 27/27 · dgb_connection_coinbase_test 8/8 (local). GPG-signed.

Consensus-bearing (touches the verifier) → surface-for-tap, not auto-merge.


Reviewer focus — decltype-deduced return type (per integrator)

The one spot where a "verbatim lift" could silently differ is the deduced Result. Confirmed byte-identical to a hand-named type:

  • share_tracker.hpp:1701 declares CumulativeWeights get_v36_decayed_cumulative_weights(...) — return by value, a concrete non-reference struct.
  • decltype(tracker.get_v36_decayed_cumulative_weights(...)) over that call-expression yields exactly CumulativeWeights (a function-call prvalue, so decltype is the declared return type, unchanged). No reference qualifier, no narrowing, no copy-vs-ref change of the weight map.
  • Proof it is a value type (not a reference): Result out{}; value-initializes the result; a reference deduction would be ill-formed and fail to compile. The 27/27 build is that proof.
  • Hand-naming CumulativeWeights directly would be behavior-identical but reintroduces the share_tracker.hpp <-> share_check.hpp include cycle — the only reason decltype is used here.

Net: emission and verification share one weight-map type by construction; the decltype is a cycle-break, not a type change.

frstrtr added a commit that referenced this pull request Jun 22, 2026
Same bar as the #328 payout-split invariance KAT: prove the #333 lift of
generate_share_transaction() step-1 into compute_pplns_weight_walk() moves
ZERO weight. Embed the pre-refactor inline walk verbatim as
legacy_inline_weight_walk() and assert the helper reproduces it weight-for-
weight (per-script weights, total_weight, total_donation_weight) across a
battery: V36 decay path, pre-V36 grandparent-start + max_weight cap,
donation split, full-donation zero-addr-weight share, the insufficient-depth
guard (both throw identically), null/absent parents, and a chain exceeding the
real_chain_length window. Each weight map is then fed through the #328
payout-split SSOT and the outputs + donation asserted byte-identical, closing
the end-to-end no-payout-satoshi-drifts proof shared by the emission and
verification paths. dgb_share_test 28/28.
frstrtr added 5 commits June 22, 2026 15:36
…T helper

Replace the inline steps 2-3 of the share verification SSOT
generate_share_transaction() (PPLNS amount math + consensus output
ordering) with a call to coin/pplns_payout_split.hpp
compute_pplns_payout_split(). The helper is a verbatim lift of that
inline math; delegating collapses the two copies into a single
consensus implementation so the per-connection Stratum coinbase
assembler and the verifier can never diverge on a payout satoshi.

Behavior-neutral by construction (same formulae, finder fee, >=1-sat
V36 donation floor + (amount, script) tiebreak, ascending sort
truncated to [-4000:]). The helper oracle-vector KAT
(dgb_pplns_payout_split_test) pins the values against p2pool-merged-v36;
the verifier-path framing tests (dgb_gentx_share_path_test,
dgb_gentx_coinbase_test) stay green, proving the share path still emits
identical coinbase framing.

Stacked on dgb/pplns-payout-split (helper extraction).
build_connection_coinbase_from_pplns delegates the payout split to
compute_pplns_payout_split() -- the same helper generate_share_transaction
(the share verification path, #329) calls -- instead of taking pre-resolved
payout_outputs/donation_amount. The coinbase a miner hashes per-connection and
the coinbase the share check enforces are now byte-identical on every payout
satoshi by construction: one payout implementation, not two kept in agreement.

Pure delegation + field forwarding into build_connection_coinbase_parts; no
inline payout arithmetic. dgb-tree-only. 4 delegation-identity KATs
(V36, pre-V36 finder fee, segwit/ref forwarding, value anchor); 8/8 green.
Stacks on the #328/#329 PPLNS SSOT stack.
…via producer seam

Replace the empty build_connection_coinbase() stub with a delegation to
build_connection_coinbase_from_pplns (the #330 SSOT that itself routes through
the single compute_pplns_payout_split the verifier calls). A new
set_pplns_inputs_fn producer seam supplies the PPLNS weight map (walked from the
ShareTracker), ref_hash, subsidy and donation script; the tracker walk stays in
main_dgb where the ShareTracker + CoinParams are in scope, so no sharechain logic
leaks into the work source.

Fenced: while the seam is UNBOUND (or returns nullopt) the method is
byte-identical to the pre-wire stub (empty job, no work pushed) -- runtime
behavior is unchanged until main_dgb binds the producer (follow-on slice). The
emitted coinb1/coinb2 are byte-identical to the verifier per a KAT comparing the
wired output to the SSOT called directly with the same inputs.

3 KATs added (empty-until-wired, byte-identical delegation, nullopt->empty);
dgb_work_source_test 26/26 green.
Extract generate_share_transaction() step-1 PPLNS tracker walk into
dgb::coin::compute_pplns_weight_walk(), the producer-side counterpart of
the steps 2-3 lift in pplns_payout_split.hpp (#328). The share-verification
path and the per-connection Stratum coinbase emission path now draw one
tracker-walk implementation, so the weight map a miner hashes cannot drift
from the one the verifier enforces.

Verbatim lift: exact V36 depth-decay vs pre-V36 grandparent-start branch,
the data.py insufficient-depth guard, and the block-target/spread max_weight
cap are unchanged. Result type is deduced from the tracker (decltype) to
avoid the share_tracker.hpp <-> share_check.hpp include cycle.

KAT WeightWalkSSOTContract locks the helper contract (verbatim V36 forward +
safe-empty on null/absent parent); the unchanged generate_share_transaction
fixtures prove value-level byte-identity. dgb_share_test 27/27,
dgb_connection_coinbase_test 8/8.
Same bar as the #328 payout-split invariance KAT: prove the #333 lift of
generate_share_transaction() step-1 into compute_pplns_weight_walk() moves
ZERO weight. Embed the pre-refactor inline walk verbatim as
legacy_inline_weight_walk() and assert the helper reproduces it weight-for-
weight (per-script weights, total_weight, total_donation_weight) across a
battery: V36 decay path, pre-V36 grandparent-start + max_weight cap,
donation split, full-donation zero-addr-weight share, the insufficient-depth
guard (both throw identically), null/absent parents, and a chain exceeding the
real_chain_length window. Each weight map is then fed through the #328
payout-split SSOT and the outputs + donation asserted byte-identical, closing
the end-to-end no-payout-satoshi-drifts proof shared by the emission and
verification paths. dgb_share_test 28/28.
@frstrtr
frstrtr force-pushed the dgb/pplns-weight-walk-ssot branch from c8d4408 to 46b7b0a Compare June 22, 2026 15:39
@frstrtr
frstrtr changed the base branch from dgb/conn-coinbase-live-wire to master June 22, 2026 15:40
@frstrtr
frstrtr marked this pull request as ready for review June 22, 2026 16:58
@frstrtr
frstrtr merged commit e007c60 into master Jun 22, 2026
12 checks passed
frstrtr added a commit that referenced this pull request Jun 23, 2026
Same bar as the #328 payout-split invariance KAT: prove the #333 lift of
generate_share_transaction() step-1 into compute_pplns_weight_walk() moves
ZERO weight. Embed the pre-refactor inline walk verbatim as
legacy_inline_weight_walk() and assert the helper reproduces it weight-for-
weight (per-script weights, total_weight, total_donation_weight) across a
battery: V36 decay path, pre-V36 grandparent-start + max_weight cap,
donation split, full-donation zero-addr-weight share, the insufficient-depth
guard (both throw identically), null/absent parents, and a chain exceeding the
real_chain_length window. Each weight map is then fed through the #328
payout-split SSOT and the outputs + donation asserted byte-identical, closing
the end-to-end no-payout-satoshi-drifts proof shared by the emission and
verification paths. dgb_share_test 28/28.
frstrtr added a commit that referenced this pull request Jun 23, 2026
dgb(phase-b): lift PPLNS weight walk into shared SSOT (step 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant